package com.llb666.shop.items.domain.service;

import com.alibaba.fastjson2.JSON;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.llb666.common.core.utils.DesensitizationUtil;
import com.llb666.common.core.utils.page.PagedGridResult;
import com.llb666.common.redis.service.RedisService;
import com.llb666.shop.cart.domain.entity.ShopCartBo;
import com.llb666.shop.items.api.vo.SearchItemsVo;
import com.llb666.shop.items.domain.entity.Items;
import com.llb666.shop.items.domain.entity.ItemsImg;
import com.llb666.shop.items.domain.entity.ItemsParam;
import com.llb666.shop.items.domain.entity.ItemsSpec;
import com.llb666.shop.items.domain.entity.bo.ItemsCommentBo;
import com.llb666.shop.items.domain.entity.bo.OrderItemsBo;
import com.llb666.shop.items.domain.repository.ItemsImgRepository;
import com.llb666.shop.items.domain.repository.ItemsParamRepository;
import com.llb666.shop.items.domain.repository.ItemsRepository;
import com.llb666.shop.items.domain.repository.ItemsSpecRepository;
import lombok.RequiredArgsConstructor;
import net.logstash.logback.encoder.org.apache.commons.lang3.ObjectUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 商品信息service.
 *
 * @author llb666
 * @version 1.0.0
 */
@Service
@RequiredArgsConstructor(onConstructor_ = {@Autowired})
public class ItemsService {

    private static final String ITEMS = "redis_items:";
    private static final String ITEMS_IMG = "redis_items_img:";
    private static final String ITEMS_PARAM = "redis_items_param:";
    private static final String ITEMS_SPEC = "redis_items_spec:";

    private final ItemsRepository itemsRepository;
    private final ItemsImgRepository itemsImgRepository;
    private final ItemsSpecRepository itemsSpecRepository;
    private final ItemsParamRepository itemsParamRepository;
    private final RedisService redisService;

    /**
     * 根据一级商品类目id列表查询商品列表信息.
     *
     * @param rootCatIdSet 一级商品类目id列表
     * @return 商品列表
     */
    @Transactional(propagation = Propagation.SUPPORTS, rollbackFor = Exception.class)
    public List<Items> getItemsByRootCatIdSet(Set<Integer> rootCatIdSet) {
        return itemsRepository.getItemsByRootCatIdSet(rootCatIdSet);
    }

    /**
     * 根据商品id列表信息查询商品图片信息.
     *
     * @param itemsIdSet 商品id列表.
     * @return 商品图片列表
     */
    @Transactional(propagation = Propagation.SUPPORTS, rollbackFor = Exception.class)
    public List<ItemsImg> getImgUrlsByItemsIds(Set<String> itemsIdSet) {
        return itemsImgRepository.getImgUrlsByItemsIds(itemsIdSet);
    }

    /**
     * 查询商品信息.
     *
     * @param itemsId 商品id
     * @return 商品基础信息
     */
    @Transactional(propagation = Propagation.SUPPORTS, rollbackFor = Exception.class)
    @Async("itemsServiceExecutor")
    public CompletableFuture<Items> queryById(String itemsId) {
        Items items;
        if (redisService.hasKey(ITEMS + itemsId)) {
            String itemsStr = redisService.getCacheObject(ITEMS + itemsId);
            items = JSON.parseObject(itemsStr, Items.class);
        } else {
            items = itemsRepository.queryById(itemsId);
            if (ObjectUtils.isEmpty(items)) {
                // 当数据库位空也缓存起来，并设置缓存时间5分钟
                redisService.setCacheObject(ITEMS + itemsId, JSON.toJSONString(items), 5L, TimeUnit.MINUTES);
            } else {
                redisService.setCacheObject(ITEMS + itemsId, JSON.toJSONString(items));
            }
        }
        return CompletableFuture.completedFuture(items);
    }

    /**
     * 查询商品图片信息.
     *
     * @param itemsId 商品ID
     * @return 商品图片信息
     */
    @Transactional(propagation = Propagation.SUPPORTS, rollbackFor = Exception.class)
    @Async("itemsServiceExecutor")
    public CompletableFuture<List<ItemsImg>> queryItemsImgById(String itemsId) {
        List<ItemsImg> itemsImgs;
        if (redisService.hasKey(ITEMS_IMG + itemsId)) {
            String itemsImgStr = redisService.getCacheObject(ITEMS_IMG + itemsId);
            itemsImgs = JSON.parseArray(itemsImgStr, ItemsImg.class);
        } else {
            itemsImgs = itemsImgRepository.queryItemsImgById(itemsId);
            if (CollectionUtils.isEmpty(itemsImgs)) {
                // 当数据库位空也缓存起来，并设置缓存时间5分钟
                redisService.setCacheObject(ITEMS_IMG + itemsId, JSON.toJSONString(itemsImgs), 5L, TimeUnit.MINUTES);
            } else {
                redisService.setCacheObject(ITEMS_IMG + itemsId, JSON.toJSONString(itemsImgs));
            }
        }
        return CompletableFuture.completedFuture(itemsImgs);
    }

    /**
     * 查询商品规格信息.
     *
     * @param itemsId 商品ID
     * @return 商品规格信息
     */
    @Transactional(propagation = Propagation.SUPPORTS, rollbackFor = Exception.class)
    @Async("itemsServiceExecutor")
    public CompletableFuture<List<ItemsSpec>> queryItemsSpecById(String itemsId) {
        // 采用hash缓存商品规格信息,因为"下单"有更新商品规格库存的操作.
        List<ItemsSpec> itemsSpecs = new ArrayList<>();
        Collection<String> keys = redisService.keys(ITEMS_SPEC + itemsId + "*");
        if (!CollectionUtils.isEmpty(keys)) {
            for (String key : keys) {
                Map<String, ItemsSpec> cacheMap = redisService.getCacheMap(key);
                ItemsSpec itemsSpec = new ObjectMapper().convertValue(cacheMap, ItemsSpec.class);
                itemsSpecs.add(itemsSpec);
            }
        } else {
            itemsSpecs = itemsSpecRepository.queryItemsSpecById(itemsId);
            if (CollectionUtils.isEmpty(itemsSpecs)) {
                // 当数据库位空也缓存起来，并设置缓存时间5分钟
                redisService.setCacheMap(ITEMS_SPEC + itemsId, new HashMap<>(0));
                redisService.expire(ITEMS_SPEC + itemsId, 5L, TimeUnit.MINUTES);
            } else {
                for (ItemsSpec itemsSpec : itemsSpecs) {
                    // 多层hash.
                    redisService.setCacheMapValue(ITEMS_SPEC + itemsSpec.getId(), "id", itemsSpec.getId());
                    redisService.setCacheMapValue(ITEMS_SPEC + itemsSpec.getId(), "itemId", itemsSpec.getItemId());
                    redisService.setCacheMapValue(ITEMS_SPEC + itemsSpec.getId(), "name", itemsSpec.getName());
                    redisService.setCacheMapValue(ITEMS_SPEC + itemsSpec.getId(), "discounts", itemsSpec.getDiscounts());
                    redisService.setCacheMapValue(ITEMS_SPEC + itemsSpec.getId(), "priceDiscount", itemsSpec.getPriceDiscount());
                    redisService.setCacheMapValue(ITEMS_SPEC + itemsSpec.getId(), "stock", itemsSpec.getStock());
                    redisService.setCacheMapValue(ITEMS_SPEC + itemsSpec.getId(), "priceNormal", itemsSpec.getPriceNormal());
                    redisService.setCacheMapValue(ITEMS_SPEC + itemsSpec.getId(), "updatedTime", itemsSpec.getUpdatedTime());
                    redisService.setCacheMapValue(ITEMS_SPEC + itemsSpec.getId(), "createdTime", itemsSpec.getCreatedTime());
                }
            }
        }

        return CompletableFuture.completedFuture(itemsSpecs);
    }

    /**
     * 查询商品参数.
     *
     * @param itemsId 商品Id
     * @return 商品参数
     */
    @Transactional(propagation = Propagation.SUPPORTS, rollbackFor = Exception.class)
    @Async("itemsServiceExecutor")
    public CompletableFuture<ItemsParam> queryItemsParamById(String itemsId) {
        ItemsParam itemsParam;
        if (redisService.hasKey(ITEMS_PARAM + itemsId)) {
            String itemsParamStr = redisService.getCacheObject(ITEMS_PARAM + itemsId);
            itemsParam = JSON.parseObject(itemsParamStr, ItemsParam.class);
        } else {
            itemsParam = itemsParamRepository.queryItemsParamById(itemsId);
            if (ObjectUtils.isEmpty(itemsParam)) {
                // 当数据库位空也缓存起来，并设置缓存时间5分钟
                redisService.setCacheObject(ITEMS_PARAM + itemsId, JSON.toJSONString(itemsParam), 5L, TimeUnit.MINUTES);
            } else {
                redisService.setCacheObject(ITEMS_PARAM + itemsId, JSON.toJSONString(itemsParam));
            }
        }
        return CompletableFuture.completedFuture(itemsParam);
    }


    /**
     * 根据商品id和评论等级查询商品评论数.
     *
     * @param id    商品id
     * @param level 评论等级
     * @return 商品评论
     */
    @Transactional(propagation = Propagation.SUPPORTS, rollbackFor = Exception.class)
    public Integer getCommentCount(String id, Integer level) {
        return itemsRepository.getCommentCount(id, level);
    }


    /**
     * 分页查询商品评论信息.
     *
     * @param itemsId  商品id
     * @param level    商品等级
     * @param page     当前页数
     * @param pageSize 每页显示条数
     * @return PagedGridResult
     */
    @Transactional(propagation = Propagation.SUPPORTS, rollbackFor = Exception.class)
    public PagedGridResult<ItemsCommentBo> queryItemCommentByPage(String itemsId, Integer level, Integer page, Integer pageSize) {
        Map<String, Object> paramsMap = new HashMap<>(2);
        paramsMap.put("itemId", itemsId);
        paramsMap.put("level", level);

        PagedGridResult<ItemsCommentBo> pageInfo = itemsRepository.queryItemComments(paramsMap, page, pageSize);
        List<ItemsCommentBo> list = pageInfo.getRows();
        //脱敏
        list.forEach(e -> e.setNickname(DesensitizationUtil.commonDisplay(e.getNickname())));
        return pageInfo;
    }

    /**
     * 通过分类id搜索商品列表
     *
     * @param catId    类目id
     * @param sort     排序
     * @param page     当前页数
     * @param pageSize 每页显示条数
     * @return PagedGridResult
     */
    @Transactional(propagation = Propagation.SUPPORTS, rollbackFor = Exception.class)
    public PagedGridResult<SearchItemsVo> searchItems(Integer catId, String sort, Integer page, Integer pageSize) {
        return itemsRepository.searchItemsByThirdCat(catId, sort, page, pageSize);
    }

    /**
     * 通过规格id搜索商品列表.（购物车）
     *
     * @param itemSpecIds 商品规格
     * @return 购物车明细列表
     */
    @Transactional(propagation = Propagation.SUPPORTS, rollbackFor = Exception.class)
    public List<ShopCartBo> queryItemsBySpecIds(String itemSpecIds) {
        // 根据规格id列表查询规格列表信息.
        List<ItemsSpec> itemsSpecs = itemsSpecRepository.getItemSpecByIds(itemSpecIds);
        // 获取商品id列表，并去重.
        Set<String> itemIdSet = itemsSpecs.stream().map(ItemsSpec::getItemId).collect(Collectors.toSet());
        // 根据商品id列表查询商品信息.
        List<Items> itemsList = itemsRepository.getItemsListByIds(itemIdSet);
        // 根据商品id列表查询商品主图信息.
        List<ItemsImg> itemsImgList = itemsImgRepository.getImgUrlsByItemsIds(itemIdSet);
        return conver(itemsSpecs, itemsList, itemsImgList);
    }

    public static List<ShopCartBo> conver(List<ItemsSpec> itemsSpecs,
                                          List<Items> itemsList,
                                          List<ItemsImg> itemsImgList) {
        // 商品Map映射，id -> name
        Map<String, String> itemsMap = itemsList.stream().collect(Collectors.toMap(Items::getId, Items::getItemName));
        // 商品图片Map映射，id -> itemsImg  主图为一对一映射
        Map<String, String> imgMap = itemsImgList.stream().collect(Collectors.toMap(ItemsImg::getItemId, ItemsImg::getUrl));

        List<ShopCartBo> shopCartBos = new ArrayList<>();
        itemsSpecs.forEach(spec -> {
            ShopCartBo shopCartBo = new ShopCartBo();
            shopCartBo.setSpecId(spec.getId());
            shopCartBo.setSpecName(spec.getName());
            shopCartBo.setPriceDiscount(spec.getPriceDiscount().toString());
            shopCartBo.setPriceNormal(spec.getPriceNormal().toString());
            shopCartBo.setItemId(spec.getItemId());
            shopCartBo.setItemName(itemsMap.get(spec.getItemId()));
            shopCartBo.setItemImgUrl(imgMap.get(spec.getItemId()));
            shopCartBos.add(shopCartBo);
        });
        return shopCartBos;
    }

    /**
     * 减库存
     *
     * @param itemSpecId 规格
     * @param buyCounts  购买数量
     */
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void decreaseItemSpecStock(String itemSpecId, int buyCounts) {
        itemsSpecRepository.decreaseItemSpecStock(itemSpecId, buyCounts);
    }

    /**
     * 查询下单需要的商品信息
     *
     * @param itemIds        商品id
     * @param itemSpecIdList 规格id
     * @return -
     */
    public OrderItemsBo getOrderItemsBo(List<String> itemIds, List<String> itemSpecIdList) {
        // 多线程批量查询.
        CompletableFuture<List<ItemsSpec>> itemsSpecsFuture = itemsSpecRepository.getItemSpecFutureByIds(itemSpecIdList);
        CompletableFuture<List<Items>> itemsFuture = itemsRepository.getItemsFutureByIds(itemIds);
        CompletableFuture<List<ItemsImg>> itemsImgFuture = itemsImgRepository.queryItemMainImgsItemsImgByIds(itemIds);

        CompletableFuture.allOf(itemsSpecsFuture, itemsFuture, itemsImgFuture);
        List<Items> itemsList = itemsFuture.join();
        List<ItemsSpec> itemsSpecList = itemsSpecsFuture.join();
        List<ItemsImg> itemsImgList = itemsImgFuture.join();

        return OrderItemsBo.cover(itemsList, itemsSpecList, itemsImgList);
    }
}
